"""Generic system for wrapping all payment gateways.

This module has been created to simplify the programming process required to
properly interact with payment gateways. It makes it as simple as possible to
process credit cards and otherwise work with payment gateways/processor data.

It uses the python money module for proper currency conversion and other
currency-related processing, and contains a subsystem for optionally helping
prevent fraudulent processing.
"""
from errors import NoGatewayError
import errors
import re

# These are the standard fields that the system currently "allows" to be
# included for gateways to get data from.
STANDARD_PROCESSING_FIELDS = {
	'id': None,
	'order_id': None,
	'first_name': None,
	'last_name': None,
	'phone_number': None,
	'email_address': None, 
	'website': None,
	'ip_address': None,
	'method': None,
	'card_number': None,
	'card_exp_month': None,
	'card_exp_year': None,
	'duplicate_time': None,
	'description': None,
	'company': None,
	'addresses': {
		'billing': {
			'street_one': None,
			'street_two': None,
			'city': None,
			'state': None,
			'zipcode': None,
			'country': None,
		},
	},
	'payment': {
		'amount': None,
		'recurring': [],
	},

	# Contains dictionaries with id, name, description, quantity, price, taxable
	'items': [],
	'gateways': [],
	'validators': [],

	'test': False,
}

class Gateway(object):
	"""Generic gateway interface.

	This is an abstract class that can be extended from in order to add new
	gateways to the system."""

	transaction = None

	data_map = {}
	"""This is a dictionary of keys consisting of possible field names in
	the Transaction.request_data dictionary, and values that this gateway uses as
	field names to process that same data. This allows multiple gateways to be
	used without needing separate data sent for each."""

	def __init__(self, transaction):
		self.transaction = transaction

	@staticmethod
	def get_classname(name):
		"""Takes the name of a payment gateway as an argument, and returns the class
		name that the gateway object should be created with. When creating new
		gateways, one should always use this method to check what your classname
		should be prior to starting coding - like so:

		>>> from payment import Gateway
		>>> print Gateway.get_classname('authorize.net')
		'AuthorizeNet'

		If a developer ignores this format, their gateway will not work.
		"""
		return re.sub('\s', '', re.sub('^\d+|[\W\.]', ' ', name).title())

	@staticmethod
	def get_by_name(name):
		"""Returns a Gateway object based on the provided gateway name"""
		classname = Gateway.get_classname(name)

		__import__('gateways.%s' % classname, globals(), locals(), [classname], -1)
		if hasattr(gateways, classname):
			return getattr(gateways, classname)

class Validator(object):
	"""A simple object that can be used to validate a transaction"""
	transaction = None

	def __init__(self, transaction):
		self.transaction = transaction

	def validate(self):
		return True

class Transaction(object):
	"""A basic object representing a transaction that has been or will be
	sent to a gateway. This is returned from any sort of processing so that
	there is a method of getting the transaction id and any other data which
	has been sent back from the payment gateway."""

	id = None
	"""The transaction ID"""

	success = None
	"""Was processing successful?"""

	request_data = []
	"""Contains all data that the system will be making use of in order to
	process a given transaction."""
	
	response_data = None
	"""This should contain a dictionary of data received from the gateway,
	assuming that the gateway responds to a request for processing."""

	def __init__(self, id=None, data={}):
		"""The object can be constructed with a transaction id if needed for the
		type of processing being used, and can also take an initial data set for
		the gateway to make use of during processing. If data is not provided
		during construction of the object, any required data must be set using
		set_data()."""

		if id:
			self.id = int(id)

		self.set(data)

	def set(self, *args):
		"""Sets a field value that will be used later on for processing. A list of
		standard field values is provided in the STANDARD_PROCESSING_FIELDS variable
		within this projects namespace."""

		argc = len(args)

		if argc < 3:
			if argc is 0:
				self.request_data = STANDARD_PROCESSING_FIELDS
			elif argc is 1:
				self.request_data = STANDARD_PROCESSING_FIELDS
				self.request_data.update(args[0])
		else:
			self.request_data[args[0]] = args[1]

		if self.request_data['id']:
			self.id = int(self.request_data['id'])

		return self

	def validate(self):
		"""Runs through any validators that are active for this transaction"""
		for validator in self.request_data['validators']:
			print dir(validator)

	def process(self):
		if len(self.request_data['gateways']) == 0:
			raise NoGatewayError('No gateway was defined for this transaction.')

		for gateway_info in self.request_data['gateways']:
			gateway = Gateway.get_by_name(gateway_info['name'])(self)
			gateway.process()

			if not self.success is None:
				break

		return self

